﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TinyROS.Messages;
using TinyROS.Exceptions;

namespace TinyROS
{
    public abstract class Node
    {
        public abstract bool IsRunning { get; }
        public abstract Dictionary<Publisher,string> PublishedTopicName { get; }
        public abstract Dictionary<Subscriber,string> SubscribedTopicName { get; }
        protected bool Initialized { get; set; } = false;
        public abstract void Run();
        public abstract void Stop();
        public abstract void Init(SystemCore master);
    }
    public abstract class Topic
    {
        public string Name;
        public abstract Type MessageType { get; }
        public abstract void AddRaw(Message rawMsg);
        public event MessageCallback<Message> MessageAddedRaw;
        protected void OnMessageAddedRaw(Message msg)
        {
            this.MessageAddedRaw?.Invoke(msg);
        }
    }
    public abstract class Publisher
    {

    }
    public abstract class Subscriber
    {

    }
    public abstract class Topic<TMessage> : Topic where TMessage : Message
    {
        public abstract void Add(TMessage msg);
        public override void AddRaw(Message rawMsg)
        {
            if(rawMsg is TMessage)
            {
                var msg = rawMsg as TMessage;
                this.Add(msg);
            }
            else
            {
                throw new MessageTypeException();
            }
        }
        public abstract TMessage Latest { get; }
        public event MessageCallback<TMessage> NewMessageAdded;
        protected virtual void OnMessageAdded(TMessage msg)
        {
            this.NewMessageAdded?.Invoke(msg);
            base.OnMessageAddedRaw(msg);
        }
        public virtual void Flush()
        {
            if(this.Latest != null)
            {
                this.NewMessageAdded?.Invoke(this.Latest);
            }
        }
    }
    public delegate void MessageCallback<TMessage>(TMessage msg) where TMessage : Message;
    
    public class Publisher<TMessage> : Publisher where TMessage : Message
    {
        public Topic<TMessage> Topic { get; private set; }  
        public Publisher()
        {

        }
        public Publisher(Topic<TMessage> topic)
        {
            this.Topic = topic;
        }
        public void SetTopic(Topic<TMessage> topic)
        {
            this.Topic = topic;
        }
        public void Talk(TMessage msg)
        {
            if(this.Topic == null)
            {
                throw new TopicNotExistException();
            }
            this.Topic.Add(msg);
        }
    }
    public class Subscriber<TMessage> : Subscriber where TMessage : Message
    {
        public enum SubscribeType { Active,Passive};
        public Topic<TMessage> Topic { get; private set; }
        public int Rate { get; set; } = 1000;
        private CancellationTokenSource Canceller;
        public MessageCallback<TMessage> Callback { get; }
        //public Subscriber(Topic<TMessage> topic)
        //{
        //    #warning 订阅者无任何行为
        //    this.Topic = topic;
        //}
        public Subscriber(MessageCallback<TMessage> callback)
        {
            this.Callback += callback;
        }
        public Subscriber(Topic<TMessage> topic, MessageCallback<TMessage> callback)
        {
            
            this.Topic = topic;
            this.Callback += callback;
        }
        public void SetTopic(Topic<TMessage> topic)
        {
            this.Topic = topic;
        }
        public void Start(SubscribeType type = SubscribeType.Active)
        {
            if(this.Topic == null)
            {
                throw new TopicNotExistException();
            }
            switch (type)
            {
                case SubscribeType.Active:           
                    this.ListenActive();
                    break;
                case SubscribeType.Passive:
                    this.Topic.NewMessageAdded += this.Callback;
                    this.Refresh();
                    break;
            }
        }
        public void Stop()
        {
            try
            {
                this.Canceller.Cancel();
                this.Topic.NewMessageAdded -= this.Callback;
            }
            catch
            {
                
            }
            
        }
        private async void ListenActive()
        {
            using (this.Canceller = new())
            {
                try
                {
                    await Task.Run(async () =>
                    {
                        while (true)
                        {                        
                            this.Canceller.Token.ThrowIfCancellationRequested();
                            try
                            {
                                var msg = this.Topic.Latest;
                                if (msg != null)
                                {
                                    this.Callback?.Invoke(msg);
                                }
                                await Task.Delay(this.Rate);
                            }
                            catch
                            {
                                throw new Exception("Subscriber Listen Error");
                            }
                        }
                    }, this.Canceller.Token);
                }
                catch (OperationCanceledException e)
                {
                    //Console.WriteLine(e.StackTrace);
                }
            }
        }
        public void Refresh()
        {
            if(this.Topic == null)
            {
                throw new TopicNotExistException();
            }
            var msg = this.Topic.Latest;
            if(msg != null)
            {
                this.Callback?.Invoke(msg);
            }
        }
    }
    public interface ICommandInteractive
    {
        void ExecuteCommand(string cmd, bool raiseError = true);
        void ExecuteCommandMute(string cmd, bool raiseError = true);
    }

}
